**********************************************************************
*** ado file for the -sdpe- command
***
*** Created: 12-14-17
*** Modified: 3-6-18
***
**********************************************************************


*************************** sdpe *************************************
cap program drop sdpe
program sdpe, rclass
	version 11
	syntax anything, [comp(varname) pct(real 50)]

	quietly {
		preserve
	}
	
	local num: word count `anything'
	local nvar = 1	/* Default is one variable in changex */
	if `num' != 3 {
		local num2 = `num' - 3
		local nvar = `nvar' + (`num2' / 4)
		if mod(`num2',4) != 0 {
			di in r _n "Each variable must have two values"
			exit 121	
		}
	}
	
	if "`e(cmd)'" == "" {		/* Last estimates not found */
		di in r _n "Must run a model before -sdpe-"
		exit 301
	}

	*** the original version of this section is from -simqi-
	local nx 0
	while "`anything'" ~= "" {
		local nx = `nx' + 1
		gettoken changes anything: anything, parse("&")

		while "`changes'" ~= "" {
			gettoken vars changes : changes    /* variable(s) */
			gettoken fun1 changes : changes   /* function 1  */   
			gettoken fun2 changes : changes   /* function 2  */		
		
			local varslist `varslist' `vars'
			local fun1list `fun1list' `fun1'
			local fun2list `fun2list' `fun2'
		}
		gettoken amper anything : anything, parse("&")       /* strip-off & */
	}

	local f1list
	tokenize `fun1list'
	local b = 1
	qui foreach n of local varslist {
		tempname newval

		_ckfun "``b''"
		_getstat `n' ``b'' `newval'

		local f1list `f1list' `newval'
		local b = `b' + 1
	}
	
	local f2list
	tokenize `fun2list'
	local b = 1
	qui foreach n of local varslist {
		tempname newval

		_ckfun "``b''"
		_getstat `n' ``b'' `newval'

		local f2list `f2list' `newval'
		local b = `b' + 1
	}	
	
	
	tempvar pr_0 pr_1 pe

	tokenize `f1list'
	local b = 1
	qui foreach n of local varslist {
		replace `n' = ``b''	
		local b = `b' + 1
	}
	_estchk `e(predict)'
	qui predict `pr_0' if e(sample)$opts

	
	tokenize `f2list'
	local b = 1
	qui foreach n of local varslist {
		replace `n' = ``b''
		local b = `b' + 1
	}
	qui predict `pr_1' if e(sample)$opts
	qui gen pe = `pr_1' - `pr_0'
	label var pe "Partial Effect"	
	
	if "`pct'" == "" {
		di
	}
	else {
		calcpctile pe `pct'
	}
	
	sum pe, det	
	return scalar ape = r(mean)
	return scalar sdpe = r(sd)
	return scalar minpe = r(min)
	return scalar maxpe = r(max)

	if "`comp'" == "" {
		di 
	}
	else {
		qui foreach s in mean sd min max {
			bys `comp': egen `s'_pe = `s'(pe)	
		}
		qui duplicates drop `comp', force
		list `comp' *_pe if e(sample)
		mkmat `comp' *_pe, matrix(tempcond)
		return matrix tempcond = tempcond
	}
	
	restore
end

********************************* _ckfun ***********************************
*! version 1.3  April 26, 1999  Michael Tomz
cap program drop _ckfun
program define _ckfun
   * Checks for valid function
   version 6.0
   args fun                                           /* function   */
   if "`fun'" == "" {
      di in r "You did not enter a function"
      exit 198
   }
   local funok 0
   if "`fun'" == "mean" { local funok 1 }             /* mean       */
   else if "`fun'" == "median" { local funok 1 }      /* median     */
   else if "`fun'" == "min" { local funok 1 }         /* minimum    */
   else if "`fun'" == "minimum" { local funok 1 }     /* minimum    */
   else if "`fun'" == "max" { local funok 1 }         /* maximum    */
   else if "`fun'" == "maximum" { local funok 1 }     /* maximum    */
   else if substr("`fun'",1,1)=="p" & index("`fun'","[")==0 {  /*percentile*/
      local ptile = substr("`fun'",2,.)
      capture confirm number `ptile'
      if _rc == 7 {
         di in r _n "`fun' invalid.  Proper syntax is " _c
         di in y "p# " in r ", where # must be a number between 0 and 100."
         exit 198
      }
      if `ptile' <= 0 | `ptile' >= 100 {
         di in r _n "`ptile' is an invalid percentile."
         di in r "Percentiles must be between 0 and 100."
         exit 198
      }
      local funok 1
   }
   else {                                                 /* # or macro     */
      capture local newval = `fun'                      /* is it a #      */
      if _rc == 0 {                                       /* make sure it's */
         capture tsunab fun : `fun'                      /*   not the 1st  */
         if _rc == 0 {                                    /*   observation  */
            di in r _n "`fun' invalid."                 /*   of a variable*/
            exit 198
         }
         local funok 1
      }
   }
   if `funok' == 0 {
      di in r _n "`fun' is not a valid function"
      exit 198
   }
end

******************************** _getstat **********************************
*! version 1.3  April 26, 1999  Michael Tomz
cap program drop _getstat
program define _getstat
   * gets a statistic  <------
   args var fun newval optns  /* optns = "if `touse' `wt'" */
   capture tsunab var : `var'
   if _rc { 
		ErrGONE `var' 
    }
   if "`fun'" == "mean" {
      su `var' `optns', meanonly
      scalar `newval' = r(mean)
   }
   else if "`fun'" == "median" {
      capture _pctile `var' `optns', p(50)
      scalar `newval' = r(r1)
   }
   else if "`fun'"=="min"|"`fun'"=="minimum" {
      su `var' `optns', meanonly
      scalar `newval' = r(min)
   }
   else if "`fun'"=="max"|"`fun'"=="maximum" {
      su `var' `optns', meanonly
      scalar `newval' = r(max)
   }
   else if substr("`fun'",1,1) == "p" & index("`fun'","[") == 0 {
      local ptile = substr("`fun'",2,.)
      _pctile `var' `optns', p(`ptile')
      scalar `newval' = r(r1)
   }
   else { 
		scalar `newval' = `fun' 
	}
   if `newval' == . { 
		ErrMISS `fun' `var' 
	}
end
	
*! version 1.3  April 26, 1999  Michael Tomz
cap program drop ErrMISS
program define ErrMISS
   version 6.0
   args fun var
   di in r _n "Not enough observations to calculate the `fun' of `var'"
   di in r "No changes were made."
   exit 2000
end	


*! version 1.3  April 26, 1999  Michael Tomz
cap program drop ErrGONE
program define ErrGONE
   version 6.0
   args var
   di in r _n "Variable `var' not found.  No changes were made."
   exit 111
end


cap program drop _estchk
program define _estchk
   version 11
   args est
	
	if "`est'" == "logit_p" | "`est'" == "probit_p" {
		global opts = ", pr"
	}

	else if "`est'" == "xtlogit_re_p" | "`est'" == "xtlogit_fe_p" | "`est'" == "xtprobit_re_p" {
		global opts = ", pu0"
	}

	else if "`est'" == "xtlogit_pa_p" {
		global opts = ", mu"
	}
	
	else di in r _n "Estimation technique not allowed by -sdpe-"
end


**************************** calcdiff ********************************
cap program drop calcdiff
program define calcdiff
	svmat tc
	qui sum tc2
	local diff = abs(r(max) - r(min))
	di `diff'
	drop tc*
end

**************************** calcpctile ******************************
cap program drop calcpctile
program calcpctile, rclass
	version 11
	args vname val
	
	tempvar _vcheck
	egen `_vcheck' = mean((`vname' <= `val') / (`vname' < .)) if e(sample)
	qui sum `_vcheck', meanonly
	return scalar pct = round(100 * r(mean), 1)
	nois di "Percentile = " round(100 * r(mean), 1)
end
